package com.crazymakercircle.demo.lock;

import com.crazymakercircle.petstore.actor.Consumer;
import com.crazymakercircle.petstore.actor.Producer;
import com.crazymakercircle.petstore.goods.Goods;
import com.crazymakercircle.petstore.goods.IGoods;
import com.crazymakercircle.util.JvmUtil;
import com.crazymakercircle.util.Print;
import com.crazymakercircle.util.ThreadUtil;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/** Created by 尼恩@疯狂创客圈. */
public class ReentrantLockPetStoreFixed {

  public static final int MAX_AMOUNT = 1; // 数据区长度

  public static void main(String[] args) throws InterruptedException {
    Print.cfo("当前进程的ID是" + JvmUtil.getProcessID());
    System.setErr(System.out);
    // 共享数据区，实例对象
    DateBuffer<IGoods> dateBuffer = new DateBuffer<>();

    // 生产者执行的动作
    Callable<IGoods> produceAction =
        () -> {
          // 首先生成一个随机的商品
          IGoods goods = Goods.produceOne();
          // 将商品加上共享数据区
          dateBuffer.add(goods);
          return goods;
        };
    // 消费者执行的动作
    Callable<IGoods> consumerAction =
        () -> {
          // 从PetStore获取商品
          IGoods goods = null;
          goods = dateBuffer.fetch();
          return goods;
        };
    // 同时并发执行的线程数
    final int THREAD_TOTAL = 20;
    // 线程池，用于多线程模拟测试
    ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_TOTAL);

    // 假定共11条线程，其中有10个消费者，但是只有1个生产者；
    final int CONSUMER_TOTAL = 11;
    final int PRODUCE_TOTAL = 10;

    for (int i = 0; i < PRODUCE_TOTAL; i++) {
      // 生产者线程每生产一个商品，间隔50ms
      threadPool.submit(new Producer(produceAction, 50));
    }
//    for (int i = 0; i < CONSUMER_TOTAL; i++) {
//      // 消费者线程每消费一个商品，间隔100ms
//      threadPool.submit(new Consumer(consumerAction, 100));
//    }

    ThreadUtil.sleepMilliSeconds(5);
    System.out.println(dateBuffer.getAmount());
  }

  // 共享数据区，类定义
  static class DateBuffer<T> {
    public static final Lock LOCK_OBJECT = new ReentrantLock();
    public static final Condition NOT_FULL = LOCK_OBJECT.newCondition();
    public static final Condition NOT_EMPTY = LOCK_OBJECT.newCondition();
    // 保存数据
    private List<T> dataList = new LinkedList<>();
    // 保存数量
    private Integer amount = 0;

    public int getAmount(){
      LOCK_OBJECT.lock();
      try{
        return amount;
      }finally{
        LOCK_OBJECT.unlock();
      }
    }

    // 向数据区增加一个元素
    public void add(T element) throws Exception {
      LOCK_OBJECT.lock();
      try {
        while (amount >= MAX_AMOUNT) {
          NOT_FULL.await();
        }
        dataList.add(element);
        amount++;
        // 发送未空通知
        NOT_EMPTY.signal();
      } finally {
        LOCK_OBJECT.unlock();
      }
    }

    /** 从数据区取出一个商品 */
    public T fetch() throws Exception {
      T element = null;
      LOCK_OBJECT.lock();
      try {
        while (amount <= 0) {
          NOT_EMPTY.await();
        }
        element = dataList.remove(0);
        amount--;
        // 发送未满通知
        NOT_FULL.signal();
      } finally {
        LOCK_OBJECT.unlock();
      }

      return element;
    }
  }
}
